package gui.typeEditors;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;

import javax.swing.BoxLayout;
import javax.swing.ComboBoxModel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.MutableComboBoxModel;

import owg.util.euclidian.V3D;


public class V3DEditor extends TypeEditor<V3D> implements ActionListener
	{
	public static interface V3DPreset
		{
		public String getName();
		public V3D getV3D(V3D oldValue);
		}
	private static enum Preset {ZERO, X, X_MINUS, Y, Y_MINUS, Z, Z_MINUS, NORMALIZE, MULTIPLY, ADD}
	private static final long serialVersionUID = -118453273518947946L;
	
	private static final NumberFormat format = new DecimalFormat("0.0");
	
	V3DEditorField field;
	StringConstantEditor presetEditor;
	String[] presets;
	V3D value;
	boolean autoNormalize;
	private HashMap<String, V3DPreset> additionalPresets;
	
	public V3DEditor(V3D initialValue, boolean autoNormalize, boolean showPresetBox, V3DPreset[] additionalPresets)
		{
		super();
		setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
		this.autoNormalize = autoNormalize;
		value = initialValue.clone();
		Preset[] presetEnums = Preset.values();
		ArrayList<String> presetList = new ArrayList<String>();
		presetList.add("missingno");
		for(int i = 0; i<presetEnums.length; i++)
			{
			if(  (autoNormalize && presetEnums[i]!=Preset.NORMALIZE) || (!autoNormalize))
				presetList.add(presetEnums[i].toString());
			}
		this.additionalPresets = new HashMap<String, V3DPreset>();
		if(additionalPresets != null)
			{
			for(V3DPreset p : additionalPresets)
				{
				presetList.add(p.getName());
				this.additionalPresets.put(p.getName(), p);
				}
			}
		presets = new String[presetList.size()];
		presetList.toArray(presets);
		
		field = new V3DEditorField(initialValue, DoubleEditor.DEFAULT_COLUMNS, true);
		field.setMinimumSize(field.getPreferredSize());
		field.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e)
				{
				V3D newVal = value.clone();
				newVal = field.getValue();
				setValue(newVal,true);
				}});
		add(field);
		
		if(showPresetBox)
			{
			presetEditor = new StringConstantEditor(null, presets, false);
			presetEditor.addActionListener(this);
			add(presetEditor);
			}
		
		setValue(initialValue, false);
		this.setToolTipTextToSubComponents("Edit this vector.");
		}
	
	/**
	 * Not to be confused with getValueString, this method is used for textual UI representation.
	 */
	private String getStringValue()
		{
		V3D val = getValue();
		return format.format(val.x())+" "+format.format(val.y())+" "+format.format(val.z())+":"+(autoNormalize?"(norm)":format.format(val.len()));
		}

	protected void setValue(V3D val, boolean fire)
		{
		V3D pVal = value;
		value = val;
		field.setValue(val);
		
		if(presetEditor != null)
			{
			ComboBoxModel<String> m = presetEditor.box.getModel();
			if(m instanceof MutableComboBoxModel)
				{
				String nv = getStringValue();
				presetEditor.ignoreEvents = true;
				((MutableComboBoxModel<String>)m).removeElementAt(0);
				((MutableComboBoxModel<String>)m).insertElementAt(nv, 0);
				presetEditor.ignoreEvents = false;
				presetEditor.setValue(nv);
				}
			else
				throw new RuntimeException("Wrong ComboBoxModel!");
			}
		
		if(fire && ((pVal==null && value!=null) || !pVal.equals(value)))
			{
			fireActionEvents();
			}
		}

	@Override
	public V3D getValue()
		{
		if(autoNormalize)
			return (V3D)value.clone().normalize();
		else
			return (V3D)value.clone();
		}
	
	/**Set value without firing an event. The reference to the vector is kept, but never changed.*/
	@Override
	public void setValue(V3D val)
		{
		setValue(val, false);
		}
	@Override
	public void actionPerformed(ActionEvent e)
		{
		if (e.getSource() != presetEditor)
			throw new IllegalArgumentException("Invalid source for event");
		if(presetEditor.box.getSelectedIndex() == 0)
			return; //Nop
		
		String p = presetEditor.getValue();
		if(p.equals(Preset.ZERO.toString()))
			setValue(V3D.ZERO.clone(), true);
		else if(p.equals(Preset.X.toString()))
			setValue(V3D.X.clone(), true);
		else if(p.equals(Preset.X_MINUS.toString()))
			setValue(V3D.NEG_X.clone(), true);
		else if(p.equals(Preset.Y.toString()))
			setValue(V3D.Y.clone(), true);
		else if(p.equals(Preset.Y_MINUS.toString()))
			setValue(V3D.NEG_Y.clone(), true);
		else if(p.equals(Preset.Z.toString()))
			setValue(V3D.Z.clone(), true);
		else if(p.equals(Preset.Z_MINUS.toString()))
			setValue(V3D.NEG_Z.clone(), true);
		else if(p.equals(Preset.NORMALIZE.toString()))
			setValue(value.clone().normalize(), true);
		else if(p.equals(Preset.MULTIPLY.toString()))
			{
			final DoubleEditor factorEditor = new DoubleEditor(1.0, 1.0);
			if(JOptionPane.showConfirmDialog(null, factorEditor, "Factor", 
					JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)
				setValue(value.clone().multiply(factorEditor.getValue()), true);
			}
		else if(p.equals(Preset.ADD.toString()))
			{
			final DoubleEditor ve0 = new DoubleEditor(0.0, 1.0, 3, true);
			final DoubleEditor ve1 = new DoubleEditor(0.0, 1.0, 3, true);
			final DoubleEditor ve2 = new DoubleEditor(0.0, 1.0, 3, true);
			final JPanel editors = new JPanel();
			editors.add(ve0);
			editors.add(ve1);
			editors.add(ve2);
			if(JOptionPane.showConfirmDialog(null, editors, "Terms", 
					JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)
				setValue(value.clone().add(ve0.getValue(), ve1.getValue(), ve2.getValue()), true);
			}
		else
			{
			if(additionalPresets.containsKey(p))
				{
				setValue(additionalPresets.get(p).getV3D(value.clone()), true);
				}
			else
				System.err.println("Warning: Unknown V3D preset op: "+p);
			}
		}
	@Override
	public void setToolTipTextToSubComponents(String text)
		{
		if(presetEditor != null)
			presetEditor.setToolTipText(text);
		String fieldTip = text;
		String fieldTipEnd = "Define the x/y/z values here.";
		int endIndex = fieldTip.lastIndexOf("</body>");
		if(endIndex != -1)
			fieldTip = fieldTip.substring(0,endIndex)+"<br/>"+fieldTipEnd+"</body></html>";
		else
			fieldTip += " "+fieldTipEnd;
			
		field.setToolTipText(fieldTip);
		}
	@Override
	public void setSubComponentsEnabled(boolean en)
		{
		if(presetEditor != null)
			presetEditor.setEnabled(en);
		field.setEnabled(en);
		}
	}
